/**
 * ReportGear(2011)
 */
package com.reportgear.report.print;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.abbp.core.utils.UnitUtils;
import net.abbp.report.model.Cell;
import net.abbp.report.model.ReportModel;
import net.abbp.report.model.cell.style.background.ColorBackground;
import net.abbp.report.model.headerfooter.ReportHF;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.reportgear.report.model.auxiliary.ColumnPropList;
import com.reportgear.report.model.auxiliary.RowPropList;
import com.reportgear.report.setting.PaperSetting;
import com.reportgear.report.setting.ReportSettings;
import com.reportgear.report.view.grid.util.PaintUtils;

/**
 * 报表打印页
 * 
 * @version 1.0 2011-3-23
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 */
public class ReportPage implements Printable {
    private static final Logger logger = LoggerFactory.getLogger(ReportPage.class.getClass());
    // 当前页
    private int currentPageNumber;
    // 总页数
    private int totalPages;
    // 分组页
    private int groupPageNumber;
    // 分组总页数
    private int groupTotalPages;

    // 报表模型
    private final ReportModel reportModel;
    // 报表配置
    private final ReportSettings reportSettings;
    private final PaperSetting paperSetting;

    private final FT columnFT;
    private final FT rowFT;

    public ReportPage(ReportModel reportModel, FT rowFT, FT columnFT) {
        this.reportModel = reportModel;
        this.reportSettings = this.reportModel.getReportSettings();
        this.paperSetting = this.reportSettings.getPaperSetting();
        this.rowFT = rowFT;
        this.columnFT = columnFT;
    }

    /**
     * 获取显示单元格列宽Array
     * 
     * @return 列宽Array
     */
    public int[] getColumnIndexArray() {
        int[] cwArr = new int[this.columnFT.getSize()];

        int ptr = 0;
        for (Integer col : this.columnFT) {
            cwArr[ptr++] = this.reportModel.getColumnWidth(col);
        }

        return cwArr;
    }

    /**
     * 获取显示单元格行高Array
     * 
     * @return 行高Array
     */
    // public int[] getRowIndexArray() {
    // int[] rhArr = new int[this.rowFT.getSize()];
    //
    // int ptr = 0;
    // for (Integer row : this.rowFT) {
    // rhArr[ptr++] = this.reportModel.getRowHeight(row);
    // }
    //
    // return rhArr;
    // }

    /**
     * 
     * @return 页脚高度(inch)
     */
    public double getFooterHeight() {
        return this.reportSettings.getFooterHeight();
    }

    /**
     * 
     * @return 页眉高度(inch)
     */
    public double getHeaderHeight() {
        return this.reportSettings.getHeaderHeight();
    }

    /**
     * @return 下边距(inch)
     */
    public double getMarginBottom() {
        return this.paperSetting.getMargin().getBottom();
    }

    /**
     * @return 左边距(inch)
     */
    public double getMarginLeft() {
        return this.paperSetting.getMargin().getLeft();
    }

    /**
     * @return 右边距(inch)
     */
    public double getMarginRight() {
        return this.paperSetting.getMargin().getRight();
    }

    /**
     * @return 上边距(inch)
     */
    public double getMarginTop() {
        return this.paperSetting.getMargin().getTop();
    }

    /**
     * @return 页面方向
     */
    public int getOrientation() {
        return this.paperSetting.getOrientation();
    }

    /**
     * @return 页面高度(inch)
     */
    public double getPaperHeight() {
        PaperSize paperSize = this.paperSetting.getPaperSize();

        if (this.getOrientation() == PageFormat.PORTRAIT) {
            return Math.max(paperSize.getHeight(), paperSize.getWidth());
        } else {
            return Math.min(paperSize.getHeight(), paperSize.getWidth());
        }
    }

    /**
     * @return 页面宽度(inch)
     */
    public double getPaperWidth() {
        PaperSize paperSize = this.paperSetting.getPaperSize();

        if (this.getOrientation() == PageFormat.PORTRAIT) {
            return Math.min(paperSize.getHeight(), paperSize.getWidth());
        } else {
            return Math.max(paperSize.getHeight(), paperSize.getWidth());
        }
    }

    /**
     * 绘制报告页面
     * 
     * @param g
     *            图形对象
     */
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;

        ColorBackground.getInstance(Color.WHITE).paint(
                g2,
                new Rectangle2D.Double(0.0D, 0.0D, UnitUtils.inch2pixel(this.getPaperWidth()), UnitUtils
                        .inch2pixel(this.getPaperHeight())));

        this.printPaint(g2, false);
    }

    // 打印
    public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
        double pageWidth = UnitUtils.inch2pt(this.getPaperWidth());
        double pageHeight = UnitUtils.inch2pt(this.getPaperHeight());

        double hScale = pageFormat.getImageableWidth() / pageWidth / UnitUtils.PIXEL_PER_PT;
        double vScale = pageFormat.getImageableHeight() / pageHeight / UnitUtils.PIXEL_PER_PT;

        Graphics2D g2 = (Graphics2D) g;

        double scale = Math.min(hScale, vScale);

        if (scale < 1.0D) {
            g2.scale(scale, scale);
        }

        this.printPaint(g2, false);

        return Printable.PAGE_EXISTS;
    }

    /**
     * 打印绘制
     * 
     * @param g2
     *            绘制对象
     * @param drawBackground
     *            是否绘制背景
     */
    public void printPaint(Graphics2D g2, boolean drawBackground) {
        double width = this.getPaperWidth() - this.getMarginLeft() - this.getMarginRight();

        // 绘制页脚页眉
        this.paintHF(g2, width);
        // 绘制内容
        this.paintContent(g2, new Rectangle2D.Double(this.getMarginLeft(),
                this.getMarginTop() + this.getHeaderHeight(), width, this.getPaperHeight() - this.getMarginTop()
                        - this.getMarginBottom() - this.getHeaderHeight() - this.getFooterHeight()), drawBackground);
    }

    /**
     * 绘制页眉页脚
     * 
     * @param g2
     * @param width
     */
    private void paintHF(Graphics2D g2, double width) {
        ReportHF header = this.reportSettings.getHeader();

        int pixelWidth = UnitUtils.inch2pixel(width);
        int pixelHeight;

        if (header != null) {
            pixelHeight = UnitUtils.inch2pixel(header.getHeight());
            header.paint(g2, new Rectangle2D.Double(UnitUtils.inch2pixel(this.getMarginLeft()), UnitUtils
                    .inch2pixel(this.getMarginTop()), pixelWidth, pixelHeight), pixelWidth,
                    this.getCurrentPageNumber(), this.getTotalPages(), this.getGroupPageNumber(), this
                            .getGroupTotalPages());
        }

        ReportHF footer = this.reportSettings.getFooter();

        if (footer != null) {
            pixelHeight = UnitUtils.inch2pixel(footer.getHeight());
            footer.paint(g2,
                    new Rectangle2D.Double(UnitUtils.inch2pixel(this.getMarginLeft()),

                    UnitUtils.inch2pixel(this.getPaperHeight() - this.getMarginBottom()) - pixelHeight, pixelWidth,
                            pixelHeight), pixelWidth, this.getCurrentPageNumber(), this.getTotalPages(), this
                            .getGroupPageNumber(), this.getGroupTotalPages());
        }
    }

    /**
     * 绘制内容
     * 
     * @param g2
     *            图形对象
     * @param rect
     *            绘制区域
     * @param drawBackground
     *            是否绘制背景
     */
    private void paintContent(Graphics2D g2, Rectangle2D rect, boolean drawBackground) {
        int startX = UnitUtils.inch2pixel(rect.getX());
        int startY = UnitUtils.inch2pixel(rect.getY());

        g2.translate(startX, startY);
        this.paintCells(g2);
        g2.translate(-startX, -startY);
    }

    /**
     * 绘制所有单元格
     * 
     * @param rect
     *            表格单元格区域
     */
    public void paintCells(Graphics2D g2) {
        Rectangle rect = new Rectangle(this.columnFT.from, this.rowFT.from, this.columnFT.to - this.columnFT.from,
                this.rowFT.to - this.rowFT.from);

        final Iterator<Cell> cellIterator = this.reportModel.intersect(rect.x, rect.y, rect.width, rect.height);

        ColumnPropList cwL = this.reportModel.getColumnPropList();
        RowPropList rhL = this.reportModel.getRowPropList();

        // 计算开始X
        int fixedWidth = 0;
        List<Integer> fixedHeaderColumnsNotInCurrPage = new ArrayList<Integer>();
        for (int index : this.reportModel.getFixedHeaderColumns()) {
            // 不包含
            if (index < this.columnFT.from || index > this.columnFT.to) {
                fixedWidth += cwL.getWidth(index);
                fixedHeaderColumnsNotInCurrPage.add(index);
            }
        }

        // 处理列尾部
        List<Integer> fixedfooterColumnsNotInCurrPage = new ArrayList<Integer>();
        for (int index : this.reportModel.getFixedFooterColumns()) {
            if (index < this.columnFT.from || index > this.columnFT.to) {
                fixedfooterColumnsNotInCurrPage.add(index);
            }
        }

        // 计算开始Y
        int fixedHeight = 0;
        List<Integer> fixedHeaderRowsNotInCurrPage = new ArrayList<Integer>();
        for (int index : this.reportModel.getFixedHeaderRows()) {
            if (index < this.rowFT.from || index > this.rowFT.to) {
                fixedHeight += rhL.getHeight(index);
                fixedHeaderRowsNotInCurrPage.add(index);
            }
        }

        // 处理行尾部
        List<Integer> fixedfooterRowsNotInCurrPage = new ArrayList<Integer>();
        for (int index : this.reportModel.getFixedFooterRows()) {
            if (index < this.rowFT.from || index > this.rowFT.to) {
                fixedfooterRowsNotInCurrPage.add(index);
            }
        }

        // 绘制固定列首
        int fx = 0;
        for (int fixedIndex : fixedHeaderColumnsNotInCurrPage) {
            if (!fixedHeaderRowsNotInCurrPage.isEmpty()) {
                int hc = 0;
                for (int row : fixedHeaderRowsNotInCurrPage) {
                    Cell cell = this.reportModel.getCell(fixedIndex, row);

                    int y = hc;
                    hc += rhL.getHeight(row);
                    int w = cwL.getRangeValue(cell.getColumn(), cell.getColumn() + cell.getColumnSpan());
                    int h = rhL.getRangeValue(cell.getRow(), cell.getRow() + cell.getRowSpan());
                    Rectangle cellRect = new Rectangle(fx, y, w, h);

                    PaintUtils.paintCellElement(g2, cell, cellRect);
                }
            }
            for (int i = this.rowFT.from, to = this.rowFT.to; i < to; i++) {
                Cell cell = this.reportModel.getCell(fixedIndex, i);

                int y = fixedHeight + rhL.getRangeValue(rect.y, cell.getRow());
                int w = cwL.getRangeValue(cell.getColumn(), cell.getColumn() + cell.getColumnSpan());
                int h = rhL.getRangeValue(cell.getRow(), cell.getRow() + cell.getRowSpan());
                Rectangle cellRect = new Rectangle(fx, y, w, h);

                PaintUtils.paintCellElement(g2, cell, cellRect);
            }

            if (!fixedfooterRowsNotInCurrPage.isEmpty()) {
                int hc = fixedHeight + rhL.getRangeValue(this.rowFT.from, this.rowFT.to);
                for (int row : fixedfooterRowsNotInCurrPage) {
                    Cell cell = this.reportModel.getCell(fixedIndex, row);

                    int y = hc;
                    hc += rhL.getHeight(row);
                    int w = cwL.getRangeValue(cell.getColumn(), cell.getColumn() + cell.getColumnSpan());
                    int h = rhL.getRangeValue(cell.getRow(), cell.getRow() + cell.getRowSpan());
                    Rectangle cellRect = new Rectangle(fx, y, w, h);

                    PaintUtils.paintCellElement(g2, cell, cellRect);
                }
            }

            fx += cwL.getWidth(fixedIndex);
        }

        // 绘制固定列尾
        fx = fixedWidth + cwL.getRangeValue(this.columnFT.from, this.columnFT.to);
        for (int fixedIndex : fixedfooterColumnsNotInCurrPage) {
            if (!fixedHeaderRowsNotInCurrPage.isEmpty()) {
                int hc = 0;
                for (int row : fixedHeaderRowsNotInCurrPage) {
                    Cell cell = this.reportModel.getCell(fixedIndex, row);

                    int y = hc;
                    hc += rhL.getHeight(row);
                    int w = cwL.getRangeValue(cell.getColumn(), cell.getColumn() + cell.getColumnSpan());
                    int h = rhL.getRangeValue(cell.getRow(), cell.getRow() + cell.getRowSpan());
                    Rectangle cellRect = new Rectangle(fx, y, w, h);

                    PaintUtils.paintCellElement(g2, cell, cellRect);
                }
            }

            for (int i = this.rowFT.from, to = this.rowFT.to; i < to; i++) {
                Cell cell = this.reportModel.getCell(fixedIndex, i);

                int y = fixedHeight + rhL.getRangeValue(rect.y, cell.getRow());
                int w = cwL.getRangeValue(cell.getColumn(), cell.getColumn() + cell.getColumnSpan());
                int h = rhL.getRangeValue(cell.getRow(), cell.getRow() + cell.getRowSpan());
                Rectangle cellRect = new Rectangle(fx, y, w, h);

                PaintUtils.paintCellElement(g2, cell, cellRect);
            }

            if (!fixedfooterRowsNotInCurrPage.isEmpty()) {
                int hc = fixedHeight + rhL.getRangeValue(this.rowFT.from, this.rowFT.to);
                for (int row : fixedfooterRowsNotInCurrPage) {
                    Cell cell = this.reportModel.getCell(fixedIndex, row);

                    int y = hc;
                    hc += rhL.getHeight(row);
                    int w = cwL.getRangeValue(cell.getColumn(), cell.getColumn() + cell.getColumnSpan());
                    int h = rhL.getRangeValue(cell.getRow(), cell.getRow() + cell.getRowSpan());
                    Rectangle cellRect = new Rectangle(fx, y, w, h);

                    PaintUtils.paintCellElement(g2, cell, cellRect);
                }
            }
            fx += cwL.getWidth(fixedIndex);
        }

        // 绘制固定行首
        int fy = 0;
        for (int fixedIndex : fixedHeaderRowsNotInCurrPage) {
            for (int i = this.columnFT.from, to = this.columnFT.to; i < to; i++) {
                Cell cell = this.reportModel.getCell(i, fixedIndex);

                int x = fixedWidth + cwL.getRangeValue(rect.x, cell.getColumn());
                int w = cwL.getRangeValue(cell.getColumn(), cell.getColumn() + cell.getColumnSpan());
                int h = rhL.getRangeValue(cell.getRow(), cell.getRow() + cell.getRowSpan());
                Rectangle cellRect = new Rectangle(x, fy, w, h);

                PaintUtils.paintCellElement(g2, cell, cellRect);
            }

            fy += rhL.getHeight(fixedIndex);
        }

        // 绘制固定行尾
        fy = fixedHeight + rhL.getRangeValue(this.rowFT.from, this.rowFT.to);
        for (int fixedIndex : fixedfooterRowsNotInCurrPage) {
            for (int i = this.columnFT.from, to = this.columnFT.to; i < to; i++) {
                Cell cell = this.reportModel.getCell(i, fixedIndex);

                int x = fixedWidth + cwL.getRangeValue(rect.x, cell.getColumn());
                int w = cwL.getRangeValue(cell.getColumn(), cell.getColumn() + cell.getColumnSpan());
                int h = rhL.getRangeValue(cell.getRow(), cell.getRow() + cell.getRowSpan());
                Rectangle cellRect = new Rectangle(x, fy, w, h);

                PaintUtils.paintCellElement(g2, cell, cellRect);
            }

            fy += rhL.getHeight(fixedIndex);
        }

        while (cellIterator.hasNext()) {
            Cell cell = cellIterator.next();

            int x = fixedWidth + cwL.getRangeValue(rect.x, cell.getColumn());
            int y = fixedHeight + rhL.getRangeValue(rect.y, cell.getRow());
            int w = cwL.getRangeValue(cell.getColumn(), cell.getColumn() + cell.getColumnSpan());
            int h = rhL.getRangeValue(cell.getRow(), cell.getRow() + cell.getRowSpan());
            Rectangle cellRect = new Rectangle(x, y, w, h);
            PaintUtils.paintCellElement(g2, cell, cellRect);
        }
    }

    /**
     * 获取当前页码
     * 
     * @return 当前页码
     */
    public int getCurrentPageNumber() {
        return this.currentPageNumber;
    }

    /**
     * 设置当前页码
     * 
     * @param currentPageNumber
     *            页码
     */
    public void setCurrentPageNumber(int currentPageNumber) {
        this.currentPageNumber = currentPageNumber;
    }

    /**
     * 
     * @return 总页数
     */
    public int getTotalPages() {
        return this.totalPages;
    }

    /**
     * 设置总页数
     * 
     * @param totalPages
     *            总页数
     */
    public void setTotalPages(int totalPages) {
        this.totalPages = totalPages;
    }

    /**
     * 分组页码
     * 
     * @param groupPageNumber
     *            分组页码
     */
    public void setGroupPageNumber(int groupPageNumber) {
        this.groupPageNumber = groupPageNumber;
    }

    /**
     * 获取分组页码
     * 
     * @return 分组页码
     */
    public int getGroupPageNumber() {
        return groupPageNumber;
    }

    /**
     * 分组总页数
     * 
     * @param groupTotalPages
     *            分组总页数
     */
    public void setGroupTotalPages(int groupTotalPages) {
        this.groupTotalPages = groupTotalPages;
    }

    /**
     * 分组总页数
     * 
     * @return 分组总页数
     */
    public int getGroupTotalPages() {
        return groupTotalPages;
    }

    public FT getColumnFT() {
        return columnFT;
    }

    public FT getRowFT() {
        return rowFT;
    }

    /**
     * 获取当前模型
     * 
     * @return 当前模型
     */
    public ReportModel getReportModel() {
        return this.reportModel;
    }
}
